﻿using System;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;

namespace Xpus.Web.Common
{
	/// <summary>
	/// 表示到xpus的web请求的上下文。
	/// </summary>
	public class XpusContext
	{
		/// <summary>
		/// 将XpusContext放到HttpContext中时使用的key。
		/// </summary>
		const string XpusContextKey = "xpus.context";

		/// <summary>
		/// 默认语言，当用户请求的URL中没有指明语言时转向该语言。
		/// </summary>
		const string DefaultCulture = "zh-cn";

		/// <summary>
		/// xpus域名的后缀（xpus.net）。
		/// </summary>
		const string XpusHostRoot = "xpus.net";

		XpusContext()
		{
		}

		/// <summary>
		/// 从当前HTTP上下文解析出xpus上下文，并存放在HTTP上下文对象中。
		/// </summary>
		internal static void CreateContext()
		{
			XpusContext context = new XpusContext();
			context.ParseContext(HttpContext.Current);
			HttpContext.Current.Items[XpusContextKey] = context;
		}

		/// <summary>
		/// 获取与当前请求相关的xpus上下文。
		/// </summary>
		public static XpusContext Current
		{
			get
			{
				return (XpusContext)HttpContext.Current.Items[XpusContextKey];
			}
		}

		/// <summary>
		/// 获取用户所访问的xpus应用程序。
		/// </summary>
		public XpusApplication Application { get; private set; }

		/// <summary>
		/// 获取用户所访问的xpus版本。
		/// </summary>
		public XpusRelease Release { get; private set; }

		/// <summary>
		/// 获取用户所访问的语言名字。
		/// </summary>
		public string CultureName { get; private set; }

		/// <summary>
		/// 获取站点的根路径，形如“http://[application].[release].xpus.net/”。
		/// 一定是以“/”结尾的。
		/// </summary>
		public string RootPath { get; private set; }

		/// <summary>
		/// 获取相对路径的绝对路径。
		/// </summary>
		/// <param name="path">相对路径。最好不要以“/”开头。</param>
		/// <returns>对应的绝对路径。</returns>
		public string GetUrl(string path)
		{
			if (path == null)
				throw new ArgumentNullException("path");

			if (path == "")
				return RootPath;

			if (path[0] == '/')
				path = path.Substring(1);

			return RootPath + path;
		}

		/// <summary>
		/// 获取当前应用程序在当前语言环境中的字符串。
		/// </summary>
		/// <param name="key">字符串的键值。</param>
		/// <returns>
		/// 返回当前语言环境下具有指定键值的字符串。
		/// 如果给定键值下不存在字符串，直接返回key值。
		/// </returns>
		public string GetCultureString(string key)
		{
			if (key == null)
				throw new ArgumentNullException("key");

			try
			{
				return this.Application.GetCultureString(this.CultureName, key);
			}
			catch
			{
				return key;
			}
		}

		/// <summary>
		/// 获取资源文件（图片、脚本等）的绝对路径。
		/// </summary>
		/// <param name="path">相对路径。</param>
		/// <returns>资源文件的绝对路径。</returns>
		public string GetResource(string path)
		{
			if (path == null)
				throw new ArgumentNullException("path");

			StringBuilder sbUrl = new StringBuilder();
			sbUrl.Append("http://res.");

			sbUrl.Append(this.Release.GetDomain());
			if (sbUrl[sbUrl.Length - 1] != '.')
				sbUrl.Append(".");

			sbUrl.Append(XpusHostRoot);

			if (path[0] != '/')
				sbUrl.Append("/");

			sbUrl.Append(path);

			return sbUrl.ToString();
		}

		/// <summary>
		/// 执行URL重写。
		/// </summary>
		/// <param name="app">待进行URL重写的HttpApplication</param>
		public static void RewriteUrl(HttpApplication app)
		{
			Uri url = app.Context.Request.Url;
			XpusApplication xapp = XpusContext.Current.Application;
			StringBuilder sbUrl = new StringBuilder();

			// 首先重定向到应用程序所在的路径
			sbUrl.AppendFormat("~/{0}", xapp.Path);

			// 重写绝对路径
			string path = url.AbsolutePath;
			foreach (Regex re in xapp.UrlRewritingRules.Keys)
			{
				if (re.IsMatch(path))
				{
					path = re.Replace(path, xapp.UrlRewritingRules[re]);
					break;
				}
			}
			sbUrl.Append(path);

			string target = sbUrl.ToString();
			// 添加查询字符串
			if (!string.IsNullOrEmpty(url.Query))
			{
				string query = url.Query;
				if (target.Contains("?"))
				{
					query = "&" + query.Substring(1);
				}

				target += query;
			}

			app.Context.RewritePath(target);
		}

		/// <summary>
		/// 从HTTP请求上下文分析xpus上下文。
		/// </summary>
		/// <param name="context">传入的HTTP请求。</param>
		private void ParseContext(HttpContext context)
		{
			ParseUrl(context);
			ParseCulture(context);

			// 解析完上下文之后，就可以生成站点根目录了。
			GenerateRootPath();
		}

		/// <summary>
		/// 解析URL。可以解析到所访问的应用程序和版本。
		/// </summary>
		/// <param name="context"></param>
		private void ParseUrl(HttpContext context)
		{
			Uri url = context.Request.Url;

			//
			// 解析主机头部分
			//

			// 传入的URL主机一般形式为：
			// [application].[release].xpus.net
			//
			// 可能出现的主机为：
			// www.xpus.net（重定向到xpus.net，通过IIS完成，代码中无需处理）
			// xpus.net
			// [release].xpus.net
			// [application].xpus.net
			// [application].[release].xpus.net
			string host = url.Host;

			if (!host.EndsWith(XpusHostRoot))
				throw new XpusInvalidUrlException(url);

			int p = host.LastIndexOf(XpusHostRoot);

			if (p < 0)
				throw new XpusInvalidUrlException(url);

			if (p > 0)  // 除xpus.net之外的所有情形，要求xpus.net前面必须是“.”。
			{
				if (host[p - 1] != '.')
					throw new XpusInvalidUrlException(url);

				string pre = host.Substring(0, p);  // 取得“.xpus.net”之前的部分。
				string[] pres = pre.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);

				if (pres.Length == 1)  // [release].xpus.net或[application].xpus.net
				{
					// 优先考虑[application].xpus.net的情况，为了最大化正式版上的速度。
					XpusApplication app = XpusApplication.GetByDomain(pres[0]);

					if (app != null)  // 找到了应用，表示用户访问的是正式版上的某个个应用。
					{
						this.Application = app;
						this.Release = XpusRelease.Release;
					}
					else  // 没有找到应用，则表示有可能是[release].xpus.net。
					{
						this.Application = XpusApplication.GetByDomain("");
						this.Release = XpusReleaseUtility.GetByDomain(pres[0]);
					}
				}
				else if (pres.Length == 2)  // [application].[release].xpus.net
				{
					XpusApplication app = XpusApplication.GetByDomain(pres[0]);

					if (app == null)
						throw new XpusInvalidUrlException(url);

					this.Application = app;
					this.Release = XpusReleaseUtility.GetByDomain(pres[1]);
				}
				else
				{
					throw new XpusInvalidUrlException(url);
				}
			}
			else  // p == 0，表示用户访问的是xpus.net。
			{
				this.Application = XpusApplication.GetByDomain("");
				this.Release = XpusRelease.Release;
			}
		}

		/// <summary>
		/// 解析语言版本。
		/// </summary>
		/// <param name="context"></param>
		private void ParseCulture(HttpContext context)
		{
			// 如果用户浏览器没有选择特定语言，则使用默认语言。
			if (context.Request.UserLanguages == null)
			{
				this.CultureName = DefaultCulture;
				return;
			}

			// 遍历用户浏览器支持的语言，返回所支持的语言。
			foreach (string lang in context.Request.UserLanguages)
			{
				string name = lang.Split(new[] { ';' })[0].ToLower();

				if (this.Application.SupportCulture(name))
				{
					this.CultureName = name;
					return;
				}
			}

			// 如果执行到这里还没返回，说明用户浏览器中选择了多种语言，但xpus都不支持。
			// 此时返回默认语言。
			this.CultureName = DefaultCulture;
		}

		/// <summary>
		/// 根据当前上下文生成站点的根路径。形如“http://[application].[release].xpus.net/”。
		/// </summary>
		/// <returns>站点根路径。该结果同时保存在RootPath属性中。</returns>
		private string GenerateRootPath()
		{
			StringBuilder sbUrl = new StringBuilder();
			sbUrl.Append("http://");

			if (!string.IsNullOrEmpty(this.Application.Domain))
			{
				sbUrl.Append(this.Application.Domain);
				sbUrl.Append(".");
			}

			string release = this.Release.GetDomain();
			if (!string.IsNullOrEmpty(release))
			{
				sbUrl.Append(release);
				sbUrl.Append(".");
			}

			sbUrl.Append(XpusHostRoot);

			this.RootPath = sbUrl.ToString();
			return this.RootPath;
		}
	}

	/// <summary>
	/// 表示无效的xpus URL。
	/// </summary>
	public class XpusInvalidUrlException : Exception
	{
		/// <summary>
		/// 获取发生错误的URL。
		/// </summary>
		public Uri Url { get; private set; }

		public XpusInvalidUrlException(Uri url)
			: base(string.Format("无效的URL（{0}）。", url))
		{
			this.Url = url;
		}
	}
}
